#pragma once

#include "ThreadPool/LogMessage.hpp"
#include "ThreadPool/ThreadPool.hpp"


#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>

#include <iostream>
#include <memory>
#include <string>


class Param {
  public:
    Param(const int& sock, const std::string& ip, const uint16_t& port)
      :_sock(sock), _ip(ip), _port(port)
    {}

    ~Param()
    {}
public:
  int _sock;
  std::string _ip;
  uint16_t _port;
};

class TCP_server {
private:
  const int MAX_BACK_LOG = 20;

  static void service(int serverSock, const std::string& clientIP, uint16_t& clientPort) {
    while(1) {
      // 直接用write和read通信
      char buffer[1024];

      ssize_t readRes = read(serverSock, buffer, sizeof(buffer) - 1);
      if(readRes > 0) {
        buffer[readRes] = 0;
      } else if(readRes == 0) {
        // 返回值等于零，对端停止，这里服务端也停止
        LogMessage(NORMAL, _F, _L, "[%s:%d] =___cient disconnected___=", clientIP.c_str(), clientPort);
        break;
      } else {
        LogMessage(ERROR, _F, _L, "server read fail");
        break;
      }
      // 先打印一下谁发过来的数据
      LogMessage(NORMAL, _F, _L, "is servering\n\t   [%s:%d] send message ::%s", clientIP.c_str(), clientPort, buffer);
      ssize_t writeRes = write(serverSock, buffer, strlen(buffer));
      if(writeRes < 0) {
        LogMessage(ERROR, _F, _L, "server send fail");
      }
    }
  }

 // static void* ThreadRoutine(void* arg) {
 //   pthread_detach(pthread_self());

 //   Param* p = reinterpret_cast<Param*>(arg);
 //   
 //   int sock = p->_sock;
 //   std::string ip = p->_ip;
 //   uint16_t port = p->_port;

 //   service(sock, ip, port);
 //   
 //   // delete掉ThreadData对象的空间，不然内存泄漏
 //   delete p;
 //   // 线程在执行完任务后，得关闭掉文件描述符，不然会文件描述符泄漏
 //   close(sock);
 // }

public:
  TCP_server(uint16_t port, const std::string ip = "", int listen_sock = -1)
    :_port(port)
    ,_ip(ip)
    ,_listen_sock(listen_sock)
    ,_pThreadPool(ThreadPool<Task>::GetThreadPoolPtr())
  {}
  void InitServer() {

    // 1. 创建套接字
             /*先AF_INET确定网络通信*/  /*这里用的是TCP，所以用SOCK_STREAM*/
    _listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if(_listen_sock == -1) {
      LogMessage(FATAL, _F, _L, "server create socket fail");
      exit(2);
    }
    LogMessage(DEBUG, _F, _L, "server create socket success, listen sock::%d", _listen_sock);

    // 2. bind 绑定IP和port
    sockaddr_in local;
    local.sin_addr.s_addr = _ip == "" ? INADDR_ANY : inet_addr(_ip.c_str());
    local.sin_port = htons(_port);
    local.sin_family = AF_INET;
    if(bind(_listen_sock, reinterpret_cast<sockaddr*>(&local), sizeof(local)) < 0) {
      LogMessage(FATAL, _F, _L, "server bind IP+port fail");
      exit(3);
    }

    LogMessage(DEBUG, _F, _L, "server bind IP+port success");

    // 3. listen为套接字设置监听状态
    if(listen(_listen_sock, MAX_BACK_LOG) < 0) {
      LogMessage(FATAL, _F, _L, "srever listen fail");
      exit(4);
    }
    LogMessage(NORMAL, _F, _L, "server init success");

  }


          /***********************线程池版******************************/
  void StartServer() {
    _pThreadPool->RunAllThread();
    while(1) {
      sockaddr_in clientMessage;
      socklen_t clientlen = sizeof(clientMessage);
      memset(&clientMessage, 0, clientlen);
      int serverSock = accept(_listen_sock, reinterpret_cast<sockaddr*>(&clientMessage), &clientlen);

      std::string clientIP = inet_ntoa(clientMessage.sin_addr);
      uint16_t clientPort = ntohs(clientMessage.sin_port);

      if(serverSock < 0) {
        LogMessage(ERROR, _F, _L, "server accept connection fail");
        continue;
      } else {
        LogMessage(NORMAL, _F, _L, "server accept connection success ::[%s:%d] server sock::%d", \
                                                                    clientIP.c_str(), clientPort,serverSock);
      }

      _pThreadPool->PushTask(Task(serverSock, clientIP, clientPort));
    }


  }

          /***********************多线程版******************************/
 // void StartServer() {
 //   while(1) {
 //     sockaddr_in clientMessage;
 //     socklen_t clientlen = sizeof(clientMessage);
 //     memset(&clientMessage, 0, clientlen);
 //     int serverSock = accept(_listen_sock, reinterpret_cast<sockaddr*>(&clientMessage), &clientlen);
 //     if(serverSock < 0) {
 //       LogMessage(ERROR, _F, _L, "server accept connection fail");
 //       continue;
 //     } else {
 //       LogMessage(NORMAL, _F, _L, "server accept connextion success, server sock::%d", serverSock);
 //     }

 //     std::string clientIP = inet_ntoa(clientMessage.sin_addr);
 //     uint16_t clientPort = ntohs(clientMessage.sin_port);
 //     pthread_t tid;
 //     Param* p = new Param(serverSock, clientIP, clientPort);
 //     pthread_create(&tid, nullptr, ThreadRoutine, reinterpret_cast<void*>(p));
 //   }
 // }


          /***********************多进程②版*****************************/
  //void StartServer() {
  //  while(1) {
  //    sockaddr_in clientMessage;
  //    socklen_t clientlen = sizeof(clientMessage);
  //    memset(&clientMessage, 0, clientlen);
  //    int serverSock = accept(_listen_sock, reinterpret_cast<sockaddr*>(&clientMessage), &clientlen);
  //    if(serverSock < 0) {
  //      LogMessage(ERROR, _F, _L, "server accept connection fail");
  //      continue;
  //    } else {
  //      LogMessage(NORMAL, _F, _L, "server accept connection success, server sock::%d", serverSock);
  //    }
  //    if(fork() == 0) {
  //      
  //      close(_listen_sock);
  //      if(fork() > 0) {
  //        exit(0);
  //      }

  //      std::string clientIP = inet_ntoa(clientMessage.sin_addr);
  //      uint16_t clientPort = ntohs(clientMessage.sin_port);
  //      service(serverSock, clientIP, clientPort);
  //      
  //      exit(0);
  //    }
  //    

  //    // 父进程此时直接等待子进程退出，非常顺畅，因为子进程刚生下来就没了，父进程直接就等到了，可以说这一步没有时间消耗
  //    waitpid(-1, nullptr, 0);
  //    close(serverSock);
  //  }
  //}

          /***********************多进程①版********************************/
//  void StartServer() {
//  // 子进程退出后，直接忽略子进程发来的信号，子进程就会自动回收其资源，效率很高
//    signal(SIGCHLD, SIG_IGN);
//    while(1) {
//      sockaddr_in clientMessage;
//      socklen_t clientlen = sizeof(clientMessage);
//      memset(&clientMessage, 0, clientlen);
//      int serverSock = accept(_listen_sock, reinterpret_cast<sockaddr*>(&clientMessage), &clientlen);
//      if(serverSock < 0) {
;//        LogMessage(ERROR, _F, _L, "server accept connection fail");
//        continue;
//      } else {
//        LogMessage(NORMAL, _F, _L, "server accept connection success, server sock::%d", serverSock);
//      }
//  
//      if(fork() == 0) {
//        // 子进程会继承父进程中的两个文件描述符，其中listenSock没有用，是让父进程用的，所以这里要关掉
//        close(_listen_sock);
//        std::string clientIP = inet_ntoa(clientMessage.sin_addr);
//        uint16_t clientPort = ntohs(clientMessage.sin_port);
//
//        service(serverSock, clientIP, clientPort);
//        
//        // 子进程退出后会变成僵尸进程，不处理会造成内存泄露，不过让父进程wait子进程的话很麻烦
//        exit(0);
//      }
//
//      //父进程waitpid去阻塞式等待时这里和第一版的单线程没什么区别，因为父进程会卡在这里
//      //但是以非阻塞方式等待时又得每次到这都要执行一下waitpid或wait函数，很麻烦
//      //所以开头直接忽略子进程退出时的信号，子进程退出时就会自动回收其空间
//      close(serverSock);   
//
//    }
//  }


          /************************单线程版*********************************/
 // void StartServer() {
 //   while(1) {

 //     // 4.accept接收连接
 //                /*客户端相关字段*/
 //     sockaddr_in clientMessage;
 //     socklen_t clientLen = sizeof(clientMessage);
 //     memset(&clientMessage, 0, clientlen);
 //     int serverSock = accept(_listen_sock, reinterpret_cast<sockaddr*>(&clientMessage), &clientLen);
 //     if(serverSock < 0) {
 //       // 这里没连接上不能说直接退出，就像张三没有揽到某个客人餐馆就不干了，所以日志等级为ERROR
 //       LogMessage(ERROR, _F, _L, "server accept connection fail");
 //       continue;
 //     } else {
 //       LogMessage(NORMAL, _F, _L, "server accept connection success, server sock::%d", serverSock);
 //     }


 //     // 连接成功了就进行通信
 //     std::string clientIP = inet_ntoa(clientMessage.sin_addr);
 //     uint16_t clientPort = ntohs(clientMessage.sin_port);
 //     service(serverSock, clientIP, clientPort);

 //     // 一定要记得通信完了之后要关闭文件描述符，
 //     close(serverSock);
 //     // 若不关，后续通信的时候这个文件描述符会一直被占用，但是却已经没用了，这种行为也被称为文件描述符泄漏
 //     // 就像在客人吃完饭之后要收拾这个客人留下来的摊子，不然别的客人没法坐了
 //   }
 // }

private:
  uint16_t _port;  // 服务端端口号
  std::string _ip; // 服务端IP地址
  int _listen_sock;
  
  std::unique_ptr<ThreadPool<Task>> _pThreadPool;
};
